﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using KTOP.Core.Domain.Matrix;
using KTOP.Core.Extensions;
using KTOP.Core.Formatters;
using KTOP.Core.Netlists;
using KTOP.Homeworks.Hw5.Logics.AlphaMagic;
using KTOP.Homeworks.Hw5.Logics.CrossMatrix;
using KTOP.Homeworks.Hw5.Logics.HamiltonianRoutes;
using KTOP.Homeworks.Hw5.Logics.Psi;

namespace KTOP.Homeworks.Hw5.Logics
{
    class HwCreationSession
    {
        private readonly Netlist _netlist;
        private RMatrix _rMatrix;
        private readonly TextWriter _tw;
        private HamiltonianRoute _hamiltonianRoute;
        private RMatrix _r2Matrix;
        private Gsh _gsh;
        private Psi.Psi _psi;
        private int[][] _alpha;

        public HwCreationSession(Netlist netlist, TextWriter tw)
        {
            _netlist = netlist;
            _tw = tw;
        }

        public void Do()
        {
            _rMatrix = RMatrix.CreateFromNetlist(_netlist);
            _hamiltonianRoute = HamiltonianRouteBuilder.Build(_rMatrix);

            if (!_hamiltonianRoute.IsIteration)
            {
                if (_hamiltonianRoute.IsEmpty)
                {
                    _tw.WriteLine("cann't find hamiltonian iteration/way");
                    return;
                }

                if (_hamiltonianRoute.IsJustAWay)
                {
                    _tw.WriteLine("found just a hamiltonian way: {0}", _hamiltonianRoute);

                    return;
                }

            }

            _r2Matrix = MatrixRebuilder.RebuildMatrix(_rMatrix, _hamiltonianRoute);

            _gsh = GshBuilder.Buind(_r2Matrix);
            _psi = PsiCalculator.Calculate(_gsh);

            _alpha = AlphaMatrixBuilder.Build(_psi);

            FormatResults();
        }

        void Magic()
        {
            var layers = new List<AlphaMatrixBuilder.InnerOuter>();

            do
            {
                PrintPsi();
                PrintAMatrix();

                layers.Add(AlphaMatrixBuilder.ReducePsi(_alpha, _psi));
                _alpha = AlphaMatrixBuilder.Build(_psi);
                
            } while (_psi.PsiValues.Any(pv => pv.Any()));

            for (var id = 0; id < layers.Count;id++ )
            {
                _tw.WriteLine("layer #{0}", id+1);
                _tw.WriteLine("inner: {0}", string.Join(", ", layers[id].Inner.Select(link => link + 1)));
                _tw.WriteLine("outer: {0}", string.Join(", ", layers[id].Outer.Select(link => link + 1)));
                _tw.WriteLine();
            }
        }

        void FormatResults()
        {
            
            PrintPrelude();
            
            PrintGshMatrix();
         
            Magic();

        }

        private void PrintPrelude()
        {
            var matrixFormatter = new MatrixFormatter();
            var netlistFormatter = new NetlistFormatter();
            var textTableFormatter = new TextTableFormatter();

            netlistFormatter.Format(_netlist, "source netlist", _tw);
            _tw.WriteLine();

            matrixFormatter.Format(_rMatrix, "r matrix", _tw);
            _tw.WriteLine();

            _tw.WriteLine("hamiltonian iteration: {0}", _hamiltonianRoute);
            _tw.WriteLine();

            matrixFormatter.Format(_r2Matrix, "r2 matrix", _tw);
            _tw.WriteLine();
        }

        private void PrintPsi()
        {
            _tw.WriteLine("psi");
            var psiValues = _psi.PsiValues
                .Select((values, psiNum) => string.Format("psi {0} = {{ {1} }}",
                                                          psiNum + 1, string.Join("; ", values.Select(v => v + 1))));

            foreach (var psiLine in psiValues)
                _tw.WriteLine(psiLine);
            _tw.WriteLine();
        }

        private void PrintAMatrix()
        {
            var textTableFormatter = new TextTableFormatter();

            _tw.WriteLine("A matrix");
            var alphaMatrixHeader =
                ExtEnumerable.One("")
                    .Concat(Enumerable.Range(1, _alpha.Length)
                                .Select(i => i.ToString()));

            var alphaMatrixTable =
                ExtEnumerable.One(alphaMatrixHeader.ToArray())
                    .Concat(_alpha.Select((row, rowNum) =>
                                          ExtEnumerable.One((rowNum + 1).ToString())
                                              .Concat(row.Select(c => c.ToString()))
                                              .ToArray()))

                    .ToArray();

            textTableFormatter.Format(_tw, alphaMatrixTable, TextTableFormatter.Borders.None);
            _tw.WriteLine();
        }

        private void PrintGshMatrix()
        {

            var textTableFormatter = new TextTableFormatter();

            _tw.WriteLine("G' map");
            var gshMapTable = _gsh.GshMap.Select(kv => new[] { (kv.Value + 1).ToString(), kv.Key.ToString() }).ToArray();
            textTableFormatter.Format(_tw, gshMapTable, TextTableFormatter.Borders.None);
            _tw.WriteLine();

            _tw.WriteLine("G' matrix");
            var gshMatrixHeader =
                ExtEnumerable.One("")
                    .Concat(Enumerable.Range(1, _gsh.GshMatrix.Count)
                                .Select(i => i.ToString()));

            var gshMatrixTable =
                ExtEnumerable.One(gshMatrixHeader.ToArray())
                    .Concat(_gsh.GshMatrix.Select((row, rowNum) =>
                                                  ExtEnumerable.One((rowNum + 1).ToString())
                                                      .Concat(row.Select(c => c.ToString()))
                                                      .ToArray()))

                    .ToArray();

            textTableFormatter.Format(_tw, gshMatrixTable, TextTableFormatter.Borders.None);
            _tw.WriteLine();

            _tw.WriteLine("cross count: {0}", _gsh.Pi);
            _tw.WriteLine();
        }
    }
}
